﻿using System.Reflection;

namespace zijian666.AnyExtensions;
/// <summary>
/// 反射扩展
/// </summary>
public static class ReflectionExtensions
{
    /// <summary>
    /// 检查成员类型是否允许空值
    /// </summary>
    /// <param name="member"> 成员信息 </param>
    /// <returns></returns>
    public static bool IsNullable(this MemberInfo member)
    {
        if (member is not (FieldInfo or PropertyInfo))
        {
            throw new ArgumentException("Member must be of type FieldInfo or PropertyInfo", nameof(member));
        }
        // 值类型不可空
        var type = member is FieldInfo field ? field.FieldType : ((PropertyInfo)member).PropertyType;

        if (type.IsValueType && Nullable.GetUnderlyingType(type) is null)
        {
            return false;
        }

        // required 关键字检查
        // [Required] 特性检查
#if NET7_0_OR_GREATER
        if (member.IsDefined(typeof(System.Runtime.CompilerServices.RequiredMemberAttribute), false)
        || member.IsDefined(typeof(System.ComponentModel.DataAnnotations.RequiredAttribute), false))
        {
            return false;
        }
#else
        if (!member.CustomAttributes.Any(a =>
                a.AttributeType.FullName == "System.ComponentModel.DataAnnotations.RequiredAttribute"
                || a.AttributeType.FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute"))
        {
            return false;
        }
#endif

        // 2. 可空引用类型检查（NullableAttribute）
        var nullableAttr = member.CustomAttributes
            .FirstOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
        if (nullableAttr != null)
        {
            var arg = nullableAttr.ConstructorArguments.First().Value;
            if (arg is byte b)
            {
                return b == 2;
            }
            else if (arg is IReadOnlyCollection<CustomAttributeTypedArgument> args && args.Count > 0)
            {
                return args.First().Value is byte b1 && b1 == 2;
            }
        }


        // 3. 可空引用类型上下文检查（NullableContextAttribute）
        var enable = EnableNullableFeature(member);

        // 如果启用了可空引用类型特性，则默认为不可空
        return !enable;
    }

    /// <summary>
    /// 获取是否启用可空引用类型特性
    /// </summary>
    public static bool EnableNullableFeature(this MemberInfo member)
    {
        if (member is null)
        {
            throw new ArgumentNullException(nameof(member));
        }
        // 查成员本身
        return EnableNullableFeature(member.GetCustomAttributes()) ??
             // 查声明类型
             EnableNullableFeature(member.DeclaringType?.GetCustomAttributes()) ??
             // 查程序集
             EnableNullableFeature(member.Module.Assembly.GetCustomAttributes()) ??
             false;
    }

    /// <summary>
    /// 获取是否启用可空引用类型特性
    /// </summary>
    public static bool EnableNullableFeature(this Type type)
    {
        if (type is null)
        {
            throw new ArgumentNullException(nameof(type));
        }
        // 查声明类型
        return EnableNullableFeature(type.GetCustomAttributes()) ??
             // 查程序集
             EnableNullableFeature(type.Module.Assembly.GetCustomAttributes()) ??
             false;
    }

    /// <summary>
    /// 获取是否启用可空引用类型特性
    /// </summary>
    public static bool EnableNullableFeature(this Assembly assembly)
    {
        if (assembly is null)
        {
            throw new ArgumentNullException(nameof(assembly));
        }
        // 查程序集
        return EnableNullableFeature(assembly.GetCustomAttributes()) ??
            false;
    }

    /// <summary>
    /// 获取是否启用可空引用类型特性
    /// </summary>
    public static bool EnableNullableFeature(this Module module)
    {
        if (module is null)
        {
            throw new ArgumentNullException(nameof(module));
        }
        // 查程序集
        return EnableNullableFeature(module.Assembly.GetCustomAttributes()) ??
            false;
    }
    /// <summary>
    /// 获取是否启用可空引用类型特性
    /// </summary>
    private static bool? EnableNullableFeature(IEnumerable<Attribute>? attributes)
    {
        if (attributes is null)
        {
            return null;
        }
        foreach (var attr in attributes)
        {
            var type = attr.GetType();
            if (type.FullName == "System.Runtime.CompilerServices.NullableContextAttribute")
            {
                var flagField = type.GetField("Flag");
                if (flagField?.GetValue(attr) is byte b)
                {
                    return b == 1;
                }
            }
        }
        return null;
    }
}
